import http from "http";
/**
 * @typedef {http.IncomingMessage} Request
 * @typedef {http.ServerResponse} Response
 * @typedef {(req: Request, res: Response) => void | Promise<void>} Middleware
 */

class Server {
  /** @type {*} */
  instance;
  /** @type {{ method: "*" | "GET" | "POST", path: string, handler: Middleware }[]} */
  routes = [];

  /**
   * @param {string | Middleware} arg1
   * @param {Middleware?} arg2
   */
  use(arg1, arg2) {
    let path, handler;
    if (typeof arg1 === "string") {
      path = arg1;
      handler = arg2;
    } else {
      path = "*";
      handler = arg1;
    }
    this.routes.push({ method: "*", path, handler });
    return this;
  }
  /**
   * @param {string} path
   * @param {Middleware} handler
   */
  get(path, handler) {
    this.routes.push({ method: "GET", path, handler });
    return this;
  }
  /**
   * @param {string} path
   * @param {Middleware} handler
   */
  post(path, handler) {
    this.routes.push({ method: "POST", path, handler });
    return this;
  }
  /**
   * @param {string} path
   * @param {Middleware} handler
   */
  put(path, handler) {
    this.routes.push({ method: "PUT", path, handler });
    return this;
  }

  /**
   * @param {number} port
   */
  listen(port) {
    this.instance = http
      .createServer(async (req, res) => {
        const { method: _method, url } = req;
        const len = this.routes.length;
        for (let i = 0; i < len; i++) {
          const { method, path, handler } = this.routes[i];
          if (
            (path === "*" || url.startsWith(path)) &&
            (method === "*" || method === _method)
          ) {
            await handler(req, res);
          }
          if (res.headersSent || res.body) {
            console.log(_method + (method === "*" ? "*" : ""), url);
            break;
          }
        }
        if ((res.headersSent && res.writable) || res.body) {
          if (res.body && typeof res.body === "object") {
            res.setHeader("content-type", "application/json");
            res.body = JSON.stringify(res.body);
          }
          return res.end(res.body);
        }
        res.writeHead(404, {
          "content-type": "text/plain",
        });
        res.end("endpoint notfound");
      })
      .on("error", (error) => {
        console.log("!!ERROR!!");
        console.log(error);
      })
      .listen(port);
  }
}

const server = new Server();
server
  .use((req, res) => {
    res.setHeader("access-control-allow-origin", "*");
  })
  .use((req) => {
    return new Promise((resolve, reject) => {
      let data = [];
      const contentType = req.headers["content-type"];
      req.on("data", (chunk) => {
        data = [...data, ...chunk];
      });
      req.on("end", () => {
        if (/json/.test(contentType)) {
          req.body = JSON.parse(Buffer.from(data).toString("utf8"));
        }
        resolve();
      });
      req.on("error", reject);
    });
  })
  .use(async () => {
    await sleep(1000);
  })
  .post("/zs/query", async (req, res) => {
    const text =
      "Lorem ipsum dolor, sit amet consectetur adipisicing elit. Adipisci ullam ut ea aut similique? Non, cum est excepturi tempore neque doloribus modi cupiditate perspiciatis laudantium facere tempora quas atque itaque!";
    res.setHeader("content-type", "text/event-stream");
    for (let i = 0; i < text.length; i++) {
      res.write(
        JSON.stringify({
          code: 200,
          type: "summary",
          data: { content: text[i], done: i === text.length - 1 },
        }),
      );
      res.write("\n");
      await sleep(10);
    }
    res.write(
      JSON.stringify({
        code: 200,
        type: "source",
        data: {
          source_urls: [
            { title: "baidu", url: "https://baidu.com" },
            { title: "google", url: "https://google.com" },
          ],
        },
      }),
    );
    res.write("\n");
    await sleep(500);
    res.write(
      JSON.stringify({
        code: 200,
        type: "recommend",
        data: { queries: ["你是谁？", "我是谁？"] },
      }),
    );
  })
  .put("/zs/feedback", (req, res) => {
    res.body = { code: 200, message: "OK" };
  })
  .get("/", (req, res) => {
    res.setHeader("content-type", "text/plain");
    res.write("aaa");
  })
  .listen(9999);

async function sleep(duration) {
  return new Promise((resolve) => {
    setTimeout(resolve, duration);
  });
}
